package org.framework.common.ext.util;

import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.beanutils.PropertyUtilsBean;
import org.apache.commons.lang3.time.DateUtils;
import org.framework.common.ext.util.DateUtil.DatePattern;
import org.jboss.logging.Logger;
import org.springframework.core.annotation.AnnotationUtils;

/**
 * 反射工具类.
 * <p>描述:</p>
 * <p>Company:上海中信信息发展股份有限公司</p>
 * @author chanlong(陈龙)
 * @date 2013年12月28日  下午2:47:17
 * @version 1.0.2013.
 */
public final class ClassUtil {

	private static Logger log = Logger.getLogger(ClassUtil.class);

	/**
	 * 用于标识字段在执行属性复制操作时是否需要忽略
	 * @author Administrator
	 */
	@Target({ElementType.FIELD, ElementType.METHOD})
	@Retention(RetentionPolicy.RUNTIME)
	public abstract @interface CopyIgnore{}
	
	/**
	 * 根据全类名加载类.
	 * @param clsname
	 * @return
	 * @throws ClassNotFoundException
	 * @author chanlong(陈龙)
	 * @date 2013年12月28日  下午2:47:05
	 */
	@SuppressWarnings("unchecked")
	public static <T> Class<T> getClass(final String clsname) {
		try {
			return (Class<T>) Class.forName(clsname);
		} catch (ClassNotFoundException e) {
			log.error("ClassUtil.getClass：未找到指定的类！", e);
			return null;
		}
	}
	
	/**
	 * 根据对象实例加载类.
	 * @param instance
	 * @return
	 * @throws ClassNotFoundException
	 * @author chanlong(陈龙)
	 * @date 2013年12月28日  下午2:46:50
	 */
	@SuppressWarnings("unchecked")
	public static <T> Class<T> getClass(final T instance) {
		return (Class<T>) instance.getClass();
	}
	
	/**
	 * 返回对象中定义的字段.
	 * @param orig
	 * @return
	 * @author chanlong(陈龙)
	 * @date 2014年7月10日  下午5:22:23
	 */
	public static Map<String,Object> getProperties(final Object orig){
		PropertyUtilsBean util = BeanUtilsBean.getInstance().getPropertyUtils();
		PropertyDescriptor[] properties = util.getPropertyDescriptors(orig);
		Map<String,Object> map = new HashMap<String, Object>();
		try {
			for (PropertyDescriptor property : properties) {
				String name = property.getName();
				
				// 排除内部类
				if ("class".equals(name)) continue;
				
				// 排除非内部属性（没有getter或setter）
				if (!util.isReadable(orig, name) || !util.isWriteable(orig, name)) continue;
				
				// 排除JPA的特殊属性
				if (name.equals("handler") || name.equals("hibernateLazyInitializer")) continue;
				
				Object value = util.getSimpleProperty(orig, name);
				map.put(name, value);
			}
		} catch (IllegalAccessException e) {
			log.error("ClassUtil.getProperties：非法访问！", e);
		} catch (InvocationTargetException e) {
			log.error("ClassUtil.getProperties：目标不正确！", e);
		} catch (NoSuchMethodException e) {
			log.error("ClassUtil.getProperties：未找到指定的方法！", e);
		}
		
		return map;
	}
	
	/**
	 * 查找指定对象的方法.
	 * @param cls
	 * @param method
	 * @param parameterTypes
	 * @return
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @author chanlong(陈龙)
	 * @date 2014年4月26日  上午9:19:33
	 */
	public static <T> Method getMethod(final Class<T> cls, final String method, final Class<?>... parameterTypes) {
		try {
			return cls.getDeclaredMethod(method, parameterTypes);
		} catch (SecurityException e) {
			log.error("ClassUtil.getMethod：方法安全限制异常！", e);
			return null;
		} catch (NoSuchMethodException e) {
			log.error("ClassUtil.getMethod：未找到指定的方法！", e);
			return null;
		}
	}
	
	/**
	 * 根据方法名访问指定对象的方法.
	 * @param obj
	 * @param method
	 * @param parameterTypes
	 * @author chanlong(陈龙)
	 * @date 2013年12月28日  下午3:56:36
	 */
	public static <T> void invoke(final T obj, final String method, final Class<?>... types) {
		try {
			obj.getClass().getDeclaredMethod(method, types).invoke(obj);
		} catch (IllegalArgumentException e) {
			log.error("ClassUtil.invoke：当前调用的方法指定的参数不正确！", e);
		} catch (SecurityException e) {
			log.error("ClassUtil.invoke：当前调用的方法不允许外部调用！", e);
		} catch (IllegalAccessException e) {
			log.error("ClassUtil.invoke：当前调用的方法的访问权限不可见！", e);
		} catch (InvocationTargetException e) {
			log.error("ClassUtil.invoke：当前调用的方法指定的目标对象不正确！", e);
		} catch (NoSuchMethodException e) {
			log.error("ClassUtil.invoke：无法找到指定的方法！", e);
		}
	}
	
	/**
	 * 根据方法名访问指定对象的方法.
	 * @param obj
	 * @param method 方法名
	 * @param paramMap 参数类型及参数值映射
	 * @author chanlong(陈龙)
	 * @date 2013年12月28日  下午2:58:39
	 */
	public static <T> void invoke(final T obj, final String method, final Map<Class<?>, Object> paramMap) {
		try {
			Class<?>[] keys = paramMap.keySet().toArray(new Class<?>[]{});
			Object[] orgs = paramMap.values().toArray(new Object[]{});
			obj.getClass().getDeclaredMethod(method,keys).invoke(obj, orgs);
		} catch (IllegalArgumentException e) {
			log.error("ClassUtil.invoke：当前调用的方法指定的参数不正确！", e);
		} catch (SecurityException e) {
			log.error("ClassUtil.invoke：当前调用的方法不允许外部调用！", e);
		} catch (IllegalAccessException e) {
			log.error("ClassUtil.invoke：当前调用的方法的访问权限不可见！", e);
		} catch (InvocationTargetException e) {
			log.error("ClassUtil.invoke：当前调用的方法指定的目标对象不正确！", e);
		} catch (NoSuchMethodException e) {
			log.error("ClassUtil.invoke：无法找到指定的方法！", e);
		}
	}
	
	/**
	 * 将Map键值对集合复制到指定的对象中并返回该对象的集合
	 * 注：Map中的键名和指定对象中的属性名相同时才会发生复制操作
	 * @param <T>
	 * @param beanClass
	 * @param result
	 * @param annotationClass
	 * @return
	 */
	public static <ENTITY, T extends Annotation> List<ENTITY> copyProperties(final Class<ENTITY> beanClass, final List<Map<String,Object>> result){
		DebugUtil.ready();
		
		List<ENTITY> list = new ArrayList<ENTITY>();
		try {
			Field[] fields = beanClass.getDeclaredFields();
			for (Map<String, Object> map : result) {
				ENTITY bean = beanClass.newInstance();
				for (Field field : fields) {
					String name = field.getName();
					Object value = map.get(name.toUpperCase());
					if(!StringUtil.isEmpty(value) && !AnnotationUtils.isAnnotationDeclaredLocally(CopyIgnore.class, field.getClass())){
						PropertyUtils.setProperty(bean, name, value);
					}
				}
				list.add(bean);
			}
		} catch (IllegalAccessException e) {
			log.error("ClassUtil.copyProperties：当前调用的setter方法的访问权限不可见！", e);
		} catch (InvocationTargetException e) {
			log.error("ClassUtil.copyProperties：当前调用的setter方法指定的目标对象不正确！", e);
		} catch (InstantiationException e) {
			log.error("ClassUtil.copyProperties：创建对象实例时异常", e);
		} catch (NoSuchMethodException e) {
			log.error("ClassUtil.copyProperties：无法找到指定setter方法！", e);
		}
		DebugUtil.debug(ClassUtil.class, "方法copyProperties()的执行时间为");
		return list;
	}
	
	/**
	 * 将源对象中的属性复制到目标对象中，
	 * 复制依据为源和目标中相同字段的值，
	 * 增强内容：可以过滤空值。
	 * 用于dbo中更新操作接口。
	 * 
	 * @param orig 源对象
	 * @param dest 目标对象
	 * @author chanlong(陈龙)
	 * @date 2014年5月2日  上午10:40:40
	 */
	public static <ENTITY> void copyProperties(final ENTITY orig, final ENTITY dest){
		PropertyUtilsBean propertyUtilsBean = BeanUtilsBean.getInstance().getPropertyUtils();
		PropertyDescriptor[] origDescriptors = propertyUtilsBean.getPropertyDescriptors(orig);
		try {
			for (int i = 0; i < origDescriptors.length; ++i) {
				String name = origDescriptors[i].getName();
				if ("class".equals(name)) {
					continue;
				}
				if ((!(propertyUtilsBean.isReadable(orig, name))) || (!(propertyUtilsBean.isWriteable(dest, name)))) {
					continue;
				}
				Object ovalue = propertyUtilsBean.getSimpleProperty(orig, name);
				Object dvalue = propertyUtilsBean.getSimpleProperty(dest, name);
				if (!StringUtil.isEmpty(ovalue) && !ovalue.equals(dvalue)) {
					BeanUtils.copyProperty(dest, name, ovalue);
				}
			}
		} catch (NoSuchMethodException e) {
			log.error(e.getMessage(), e);
		} catch (IllegalAccessException e) {
			log.error(e.getMessage(), e);
		} catch (InvocationTargetException e) {
			log.error(e.getMessage(), e);
		}
	}
	
	/**
	 * 将源对象中的属性复制到目标对象中.
	 * 源对象：通过JdbcUtil查询得到的结果
	 * 目标对象：返回结果的实体对象
	 * @param orig
	 * @param dest
	 * @author chanlong(陈龙)
	 * @date 2014年5月7日  下午11:03:40
	 */
	public static <ENTITY> void copyProperties(final Map<String,Object> orig, final ENTITY dest, final boolean isUpperCase){
		try {
			PropertyDescriptor[] properties = PropertyUtils.getPropertyDescriptors(dest);
			for (PropertyDescriptor property : properties) {
				String name = property.getName();
				Class<?> type = property.getPropertyType();
				Object value = orig.get(isUpperCase ? name.toUpperCase() : name);
				
				if (type.isInstance(new Date()) && value instanceof Long) {
					value = new Date(Long.valueOf(String.valueOf(value)));
				} else if (type.isInstance(new Date()) && value instanceof String) {
					value = DateUtils.parseDate(String.valueOf(value), DatePattern.DATETIME.value());
				}
				
				if(StringUtil.isNotEmpty(value)) BeanUtils.setProperty(dest, name, value);
			}
		} catch (IllegalAccessException e) {
			log.error(e.getMessage(), e);
		} catch (InvocationTargetException e) {
			log.error(e.getMessage(), e);
		} catch (ParseException e) {
			log.error(e.getMessage(), e);
		}
	}

	/**
	 * 设置实体对象的值.
	 * @param bean
	 * @param name
	 * @param value
	 * @author chanlong(陈龙)
	 * @date 2014年6月20日  下午3:00:08
	 */
	public static <ENTITY> void setProperty(final ENTITY bean, final String name, final String value){
		try {
			PropertyUtils.setProperty(bean, name, value);
		} catch (IllegalAccessException e) {
			log.error(e.getMessage(), e);
		} catch (InvocationTargetException e) {
			log.error(e.getMessage(), e);
		} catch (NoSuchMethodException e) {
			log.error(e.getMessage(), e);
		}
	}
	
	public static void main(String[] args) {
		//ClassUtil.copyProperties(null,view);
		//System.out.println(view.getId());
	}
}
